#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel_list.h"

// 读者链表
struct list_head *user_list = NULL;

struct book                // 保存 √
{
    char name[20];
    struct list_head list; // 图书链表
};

struct student
{
    int age;
    char name[20];

    int borrow_books_num;    // 某个读者所借图书的个数
    struct list_head *books; // 某个读者所借图书的链表
};

// 用户节点
struct user
{
    struct student stu;    // 保存 √
    struct list_head list; // 用户链表
};

void show_list(void)
{
    printf("读者: \n");
    if(list_empty(user_list))
    {
        printf("empty\n");
        return;
    }

    struct list_head *u_pos;
    struct list_head *b_pos;
    list_for_each(u_pos, user_list)
    {
        struct user *u = list_entry(u_pos, struct user, list);
        printf("[%d, %s]: ", u->stu.age, u->stu.name);

        if(list_empty(u->stu.books))
        {
            printf("无借书\n");
            continue;
        }

        // 循环输出当前读者所借图书
        list_for_each(b_pos, u->stu.books)
        {
            struct book *b = list_entry(b_pos, struct book, list);
            printf("%s ", b->name);
        }
        printf("\n");
    }
}

// 申请一个新的用户节点，
// 且返回节点内部的小结构体指针
struct list_head *new_user(int age, const char *name)
{
    struct user *u = malloc(sizeof(struct user));
    if(u == NULL)
    {
        perror("申请节点失败");
        return NULL;
    }

    u->stu.age = age;
    strncpy(u->stu.name, name, 20);
    u->stu.borrow_books_num = 0;

    // 初始化本读者的list标准小构体，使其形成自身循环
    INIT_LIST_HEAD(&u->list);

    // 本读者所借图书的链表头指针，初始化为指向头结点
    u->stu.books = malloc(sizeof(struct list_head));
    INIT_LIST_HEAD(u->stu.books);

    return &u->list;
}

// 申请一个新的图书节点，
// 且返回节点内部的小结构体指针
struct list_head *new_book(const char *bookname)
{
    struct book *b = malloc(sizeof(struct book));
    if(b == NULL)
    {
        perror("申请节点失败");
        return NULL;
    }

    strncpy(b->name, bookname, 20);

    // 初始化本图书节点的list标准小构体，使其形成自身循环
    INIT_LIST_HEAD(&b->list);

    return &b->list;
}

void save_list(void)
{
    FILE *fp = fopen("user.dat", "w");
    if(fp == NULL)
    {
        perror("打开 user.dat 失败");
        return;
    }

    struct list_head *u_pos;
    struct list_head *b_pos;
    list_for_each(u_pos, user_list)
    {
        struct user *u = list_entry(u_pos, struct user, list);
        fwrite(&u->stu, sizeof(struct student), 1, fp);

        // 若当前读者没有借书，那么就循环保存下一个读者
        // if(list_empty(u->stu.books))
        if(u->stu.borrow_books_num == 0)
            continue;
        
        // 如果该读者有借图书，那么就将所借图书循环保存到文件
        list_for_each(b_pos, u->stu.books)
        {
            struct book *b = list_entry(b_pos, struct book, list);
            fwrite(b, sizeof(struct book), 1, fp);
        }
    }

    fclose(fp);
}


void load_list(void)
{
    FILE *fp = fopen("user.dat", "r");
    if(fp == NULL)
        return; // 首次运行，无此文件，直接返回主程序

    // 循环地从文件中读取struct student数据
    struct student stu_tmp;
    struct book   book_tmp;
    while(1)
    {
        // 从文件读取一个读者信息
        int n = fread(&stu_tmp , sizeof(struct student), 1, fp);
        if(n == 0)
            break;

        // 将读者信息新建链表节点
        struct list_head *u_pos = new_user(stu_tmp.age, stu_tmp.name);

        // 将链表节点链入链表末尾
        list_add_tail(u_pos, user_list);

        // 如果该读者没有借书，那么就处理下一个读者
        if(stu_tmp.borrow_books_num == 0)
            continue;
        
        // 否则，就循环地读取他所借图书，并链入该读者节点的books链表中
        struct user *u = list_entry(u_pos, struct user, list);
        u->stu.borrow_books_num = stu_tmp.borrow_books_num; // 将临时节点数据保存到链表节点中
        for(int i=0; i<(u->stu.borrow_books_num); i++)
        {
            // 新建图书节点
            fread(&book_tmp, sizeof(struct book), 1, fp);
            //fflush(fp);
            struct list_head *b = new_book(book_tmp.name);

            // 链入表尾
            list_add_tail(b, u->stu.books);
        }
    }

    fclose(fp);
}

int main(int argc, char const *argv[])
{
    // 得到一个具备头节点(小结构体)的空链表
    user_list = malloc(sizeof(struct list_head));
    INIT_LIST_HEAD(user_list);

    // 加载读者数据（有哪些人、分别借了那些书）
    load_list();
    show_list();

    // 循环地输入一些读者、及其所借图书
    int age;
    char name[20];
    while(1)
    {
        int ret = scanf("%d %s", &age, name);
        if(ret != 2)
            break;
        
        struct list_head *cur_user = new_user(age, name);
        list_add_tail(cur_user, user_list);

        printf("依次输入所借图书，#结束\n");
        while(1)
        {
            char bookname[20];
            scanf("%s", bookname);

            if(bookname[0] == '#')
                break;

            // 搞个图书节点并链入当前用户所借图书链表的末尾
            struct user *u = list_entry(cur_user, struct user, list);
            struct list_head *newbook = new_book(bookname);

            list_add_tail(newbook, u->stu.books);
            u->stu.borrow_books_num++;
        }

        show_list();
    }

    // 正常退出系统的时候，要储存数据
    save_list();
    
    return 0;
}
